package com.opennews.openplatform.service;

import com.opennews.openplatform.entity.tables.records.UserRoleRecord;
import com.opennews.openplatform.jooq.DSLContextWrapper;
import lombok.RequiredArgsConstructor;
import org.jooq.DSLContext;
import org.springframework.stereotype.Service;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;

import static com.opennews.openplatform.entity.Tables.ROLE;
import static com.opennews.openplatform.entity.Tables.USER_ROLE;

@RequiredArgsConstructor
@Service
public class UserRoleService {
    private final DSLContext dslContext;

    /**
     * Queries role ids of user.
     *
     * @param userId: The string of the user id.
     * @return The list of string of role id.
     */
    public List<String> queryRoleIds(String accountGroupId, String userId) {
        return dslContext.selectFrom(USER_ROLE)
                .where(
                        USER_ROLE.ACCOUNT_GROUP_ID.eq(accountGroupId)
                                .and(USER_ROLE.USER_ID.eq(userId))
                )
                .fetch(USER_ROLE.ROLE_ID);
    }

    /**
     * Queries roles' id, authoritym title based on provided user id against specific account group.
     *
     * @param accountGroupId: The string of the account group id.
     * @param userId:         The string of the user id.
     * @return The list of map instance which contains keys below.
     * id: String of the role id.
     * id: String of the role authority.
     * title: String of the role title.
     */
    public List<Map<String, Object>> queryFullRoles(String accountGroupId, String userId) {
        return dslContext.select(
                        USER_ROLE.ROLE_ID.as("id"),
                        ROLE.AUTHORITY.as("authority"),
                        ROLE.TITLE.as("title")
                )
                .from(USER_ROLE)
                .join(ROLE).on(USER_ROLE.ROLE_ID.eq(ROLE.ID))
                .where(
                        USER_ROLE.ACCOUNT_GROUP_ID.eq(accountGroupId)
                                .and(USER_ROLE.USER_ID.eq(userId))
                )
                .fetchMaps();
    }

    /**
     * Check if the user with provided id has the specific role.
     *
     * @param userId:   The String of the user id.
     * @param roleName: The string of the role name used to check if the user has it.
     * @return The bool value. True means the user has the role. False means no.
     */
    public boolean hasRole(String accountGroupId, String userId, String roleName) {
        Integer count = dslContext.selectCount()
                .from(USER_ROLE)
                .join(ROLE).on(USER_ROLE.ROLE_ID.eq(ROLE.ID))
                .where(
                        USER_ROLE.ACCOUNT_GROUP_ID.eq(accountGroupId)
                                .and(USER_ROLE.USER_ID.eq(userId))
                                .and(ROLE.AUTHORITY.eq(roleName))
                )
                .fetchOne(0, Integer.class);

        return count != null && count > 0;
    }

    /**
     * Adds roles for user.
     *
     * @param accountGroupId: The string of the account group.
     * @param userId:         The string of the user.
     * @param roleIds:        The string of role id list.
     */
    public void add(String accountGroupId, String userId, List<String> roleIds) {
        List<UserRoleRecord> records = new ArrayList<>();

        for (String roleId : roleIds) {
            UserRoleRecord record = dslContext.newRecord(USER_ROLE);
            record.setAccountGroupId(accountGroupId);
            record.setUserId(userId);
            record.setRoleId(roleId);
            records.add(record);
        }

        dslContext.batchInsert(records).execute();
    }

    /**
     * Deletes user role by user id and role ids.
     *
     * @param accountGroupId: The string of the account group.
     * @param userId:         The string of user id.
     * @param roleIds:        The string list of role ids.
     */
    void delete(String accountGroupId, String userId, List<String> roleIds) {
        dslContext.deleteFrom(USER_ROLE)
                .where(
                        USER_ROLE.ACCOUNT_GROUP_ID.eq(accountGroupId)
                                .and(USER_ROLE.USER_ID.eq(userId))
                                .and(USER_ROLE.ROLE_ID.in(roleIds))
                )
                .execute();
    }
}
